import grok
from persistent.dict import PersistentDict
from zope.interface import Interface
from zope import schema
from zope.app.container.interfaces import INameChooser
from grok import index
from zope.app.catalog.interfaces import ICatalog
from zope.component import getUtility
from operator import add

class FinancialHistory(grok.Application, grok.Container):
    def __init__(self, initialBalance=0):
        super(FinancialHistory, self).__init__()
        self.cashOnHand = initialBalance
        self.incomes = PersistentDict()

    def receiveFrom(self, source, amount):
        self.incomes[source] = self.totalReceivedFrom(source) + amount
        self.cashOnHand += amount
        
    def spendFor(self, reason, amount):
        name = reason.replace(' ','_')
        expense = Expense(reason=reason, amount=amount, audited=False)
        name = INameChooser(self).chooseName(name, expense)
        self[name] = expense
        
    def totalIncome(self):
        return reduce(add, self.incomes.values(),0)
        
    def totalExpenses(self):
        return reduce(add, [expense.amount for expense in self.values()], 0)

    def totalReceivedFrom(self, source):
        return self.incomes.get(source, 0)
    
    def totalSpentFor(self, reason):
        total = 0.0
        catalog = getUtility(ICatalog)
        results = catalog.searchResults(reason=reason)
        for object in results:
            total += object.amount
        return total


class Index(grok.View):
    grok.context(FinancialHistory)
    
    def cashOnHand(self):
        return '%0.2f' % (self.context.cashOnHand)

    def update(self, source=None, amount=None):
        if source is not None:
            self.context.receiveFrom(source,float(amount))
                    
    def listIncome(self):
        return [{'source':k, 'amount': '%0.2f'%v}
            for k,v in self.context.incomes.items()]

    def listExpenses(self):
        return self.context.values()
            
class IExpense(Interface):
    reason = schema.TextLine(title=u"Reason",
                             required=True)
    amount = schema.Float(title=u"Amount",
                          required=True,
                          constraint=lambda x: x>0)
    audited = schema.Bool(title=u"Audited",
                             required=False,
                             default=False)                                 
            
class Expense(grok.Model):
    grok.implements(IExpense)
    
    def __init__(self, reason, amount, audited=False):
        self.reason = reason
        self.amount = amount
        self.audited = audited
            
class Edit(grok.EditForm):
    grok.context(Expense)
    pass
    
class AddExpense(grok.AddForm):
    grok.context(FinancialHistory)
    
    form_fields = grok.AutoFields(IExpense).omit('date')

    @grok.action('Save')
    def add(self, **data):
        self.context.spendFor(data['reason'], data['amount'])
        self.redirect(self.application_url())
        
@grok.subscribe(IExpense, grok.IObjectAddedEvent)
def expenseAdded(expense, event):
    account = expense.__parent__
    account.cashOnHand -= expense.amount

class ExpensesIndexes(grok.Indexes):
    grok.site(FinancialHistory)
    grok.context(IExpense)

    reason = index.Text()